﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.IO;
using BLL = KC.Library.Net.Web;

namespace KC.Presentation.ImageDownloader
{
	public partial class Form1 : Form
	{
		private List<string>
			UrlBuffer = new List<string>(), UrlBufferDone = new List<string>(),
			ImageBuffer = new List<string>(), ImageBufferDone = new List<string>(),
			ProcBuffer = new List<string>(), ProcBufferDone = new List<string>(),
			DeleteBuffer = new List<string>();
		private bool isDeleteRunning = false;
		public Form1()
		{
			InitializeComponent();
			//try { System.Diagnostics.Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)7; } // Run on CPUs 0 and 1 only
			//catch { } // user only has one CPU or does not have access
		}

		private void bgSpider_DoWork(object sender, DoWorkEventArgs e)
		{
			DateTime started = DateTime.Now;
			if (UrlBuffer.Count == 0 && !UrlBufferDone.Contains(txtURL.Text.Trim())) UrlBuffer.Add(txtURL.Text.Trim());
			if (UrlBuffer.Count == 0) return;
			var urls = UrlBuffer.Take(20).ToArray();
			int j = 0;
			Parallel.For(0, urls.Count(), new ParallelOptions() { MaxDegreeOfParallelism = urls.Length }, u => {
				UrlBufferDone.Add(urls[u]);
				if (bgSpider.CancellationPending) return;
				bgSpider.ReportProgress(-1, "Spidering " + u.ToString() + "/" + urls.Count() + ": " + urls[u]);
				string body;
				try { body = new WebClient().DownloadString(new Uri(urls[u])); }
				catch (WebException) {  // 404, 500, etc.
					UrlBuffer.Remove(urls[u]);
					UrlBufferDone.Remove(urls[u]);
					return; }
				if (body.ToLower().Contains("<body")) body = body.Substring(body.ToLower().IndexOf("<body"));
				if (body.Length > 50000) body = body.Substring(0, 50000);
				List<string>
					newlinks = BLL.GetLinksFromHtml(body, urls[u]),
					images = BLL.GetImagesFromHtml(body, urls[u]);
				UrlBuffer.AddRange(newlinks.Where(l => !UrlBuffer.Contains(l) && !UrlBufferDone.Contains(l) && !BLL.isUrlBanned(l)));
				UrlBuffer.RemoveAll(l => urls.Contains(l));
				try { ImageBuffer.AddRange(images.Where(i => !ImageBuffer.Contains(i) && !BLL.isUrlBanned(i) && !ImageBufferDone.Contains(i))); }
				catch { }
				j++;
				bgSpider.ReportProgress(CalcPerc(j, urls.Count()), "Spidered " + urls[u]); });
			ImageBuffer.AddRange(UrlBuffer.Where(u => u.ToLower().EndsWith(".png") || u.ToLower().EndsWith(".jpg") || u.ToLower().EndsWith(".tif") || u.ToLower().EndsWith(".tiff") || u.ToLower().EndsWith(".bmp")));
			UrlBuffer.RemoveAll(u => ImageBuffer.Contains(u));
			bgSpider.ReportProgress(100, "Completed in " + (DateTime.Now - started).TotalSeconds.ToString() + " sec");
		}
		private void bgSpider_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			if (e.ProgressPercentage >= 0) pbSpider.Value = e.ProgressPercentage;
			if (e.UserState != null) lblSpider.Text = (string)e.UserState;
		}
		private void bgSpider_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			
		}

		private void bgProcessor_DoWork(object sender, DoWorkEventArgs e)
		{
			DateTime started = DateTime.Now;
			if (ImageBuffer.Count < 0 || ProcBuffer.Count > 100) return;
			var images = ImageBuffer.Take(16).ToArray();
			if (images.Length == 0) return;
			int j = 0;
			Parallel.For(0, images.Count(), new ParallelOptions() { MaxDegreeOfParallelism=images.Length}, i => {
				if (bgProcessor.CancellationPending) return;
				if (images[i] == null) return;
				string filename = Path.Combine(txtPath.Text, "tmp" + i.ToString() + DateTime.Now.Ticks.ToString() + images[i].Substring(images[i].LastIndexOf('.')));
				try { new WebClient().DownloadFile(images[i], filename); }
				catch { 
					ImageBufferDone.Add(images[i]);
					ImageBuffer.Remove(images[i]);
					return; }
				Image image = null;
				try { image = Image.FromFile(filename); }
				catch { ImageBuffer.Remove(images[i]); return; }
				if (((image.Width >= 480 && image.Height >= 640) || (image.Width >= 640 && image.Height >= 480)) && !ProcBuffer.Contains(filename) && !ProcBufferDone.Contains(filename))
					ProcBuffer.Add(filename);
				else DeleteBuffer.Add(filename);
				ImageBuffer.Remove(images[i]);
				bgProcessor.ReportProgress(CalcPerc(j, images.Length), "Processed " + filename); });
			bgProcessor.ReportProgress(100, "Completed in " + (DateTime.Now - started).TotalSeconds.ToString() + " sec");
		}
		private void bgProcessor_ProgressChanged(object sender, ProgressChangedEventArgs e)
		{
			pbProcess.Value = e.ProgressPercentage;
			if (e.UserState != null) lblProcess.Text = (string)e.UserState;
			if (cbRender.Checked && e.UserState != null && ((string)e.UserState).StartsWith("Processed "))
				try { picThumb.Image = Image.FromFile(((string)e.UserState).Substring(10).Trim()); }
				catch { }
		}
		private void bgProcessor_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
		}

		private void timer1_Tick(object sender, EventArgs e)
		{
			if (cbSpider.Checked && !bgSpider.IsBusy) bgSpider.RunWorkerAsync();
			if (cbProcess.Checked && !bgProcessor.IsBusy) bgProcessor.RunWorkerAsync();
			if (cbRender.Checked && pic.Image == null && ProcBuffer.Count > 0) {
				try { pic.Image = Image.FromFile(ProcBuffer[0]); }
				catch { }
				lblRender.Text = pic.Image.Width.ToString() + " x " + pic.Image.Height.ToString() + " - " + ProcBuffer[0]; }
			UpdateBuffers();
		}
		private void timer2_Tick(object sender, EventArgs e)
		{
			if (isDeleteRunning) return;
			isDeleteRunning = true;
			List<string> deleted = new List<string>();
			try {
				Parallel.ForEach(DeleteBuffer, new ParallelOptions() { MaxDegreeOfParallelism = 64 }, f => {
					try {
						if (System.IO.File.Exists(f)) File.Delete(f); 
						deleted.Add(f);
						DeleteBuffer.Remove(f); }
					catch { } }); }
			catch (AggregateException) {}
			try { DeleteBuffer.RemoveAll(f => deleted.Contains(f)); }
			catch { }
			isDeleteRunning = false;
		}

		private void btnNo_Click(object sender, EventArgs e)
		{
			if (pic.Image == null) return;
			pic.Image = null;
			DeleteBuffer.Add(ProcBuffer[0]);
			ProcBufferDone.Add(ProcBuffer[0]);
			ProcBuffer.RemoveAt(0);
		}
		private void btnYes_Click(object sender, EventArgs e)
		{
			if (pic.Image == null) return;
			pic.Image = null;
			ProcBufferDone.Add(ProcBuffer[0]);
			ProcBuffer.RemoveAt(0);
		}
		private void UpdateBuffers()
		{
			// URL buffer
			int perc = CalcPerc(UrlBuffer.Count, 1000);
			pbURL.Value = perc > 100 ? 100 : perc;
			lblURL.Text = UrlBuffer.Count.ToString();
			// URL Done buffer
			perc = CalcPerc(UrlBufferDone.Count, 1000);
			pbURL2.Value = perc > 100 ? 100 : perc;
			lblURL2.Text = UrlBufferDone.Count.ToString();
			// Image buffer
			perc = CalcPerc(ImageBuffer.Count, 1000);
			pbImage.Value = perc > 100 ? 100 : perc;
			lblImage.Text = ImageBuffer.Count.ToString();
			// Image done buffer
			perc = CalcPerc(ImageBufferDone.Count, 1000);
			pbImage2.Value = perc > 100 ? 100 : perc;
			lblImage2.Text = ImageBufferDone.Count.ToString();
			// Processor buffer
			perc = CalcPerc(ProcBuffer.Count, 1000);
			pbProc.Value = perc > 100 ? 100 : perc;
			lblProc.Text = ProcBuffer.Count.ToString();
			// Image done buffer
			perc = CalcPerc(ProcBufferDone.Count, 1000);
			pbProc2.Value = perc > 100 ? 100 : perc;
			lblProc2.Text = ProcBufferDone.Count.ToString();
			// Delete buffer
			perc = CalcPerc(DeleteBuffer.Count, 100);
			pbDelete.Value = perc > 100 ? 100 : perc;
			lblDelete.Text = DeleteBuffer.Count.ToString();
		}

		private int CalcPerc(double num, double max) { return (int)((num / max) * 100); }

		private void cbSpider_CheckedChanged(object sender, EventArgs e)
		{
			if (!cbSpider.Checked && bgSpider.IsBusy) bgSpider.CancelAsync();
		}

		private void cbProcess_CheckedChanged(object sender, EventArgs e)
		{
			if (!cbProcess.Checked && bgProcessor.IsBusy) bgProcessor.CancelAsync();
		}

		private void cbRender_CheckedChanged(object sender, EventArgs e)
		{
			if (!cbRender.Checked) {
				pic.Image = null;
				picThumb.Image = null; }
		}
	}
}
